#include "crypt.h"
#include <stdio.h>
#include <pthread.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>

#include <string.h>
#include <errno.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/err.h>

#include <openssl/des.h>

#define LEN_OF_KEY 24

// encrypt
Encrypt rsaEncrypt(char *data, char *key_path) {
    Encrypt encrypt;
    char *encrypted_data = NULL;
    RSA *rsa_key = NULL;
    FILE *file = NULL;
    int length = 0;

    // 1.打开秘钥文件
    if ((file = fopen(key_path, "rb")) == NULL) {
        perror("fopen() error");
        goto End;
    }

    // 2.从公钥中获取 加密的秘钥
    if ((rsa_key = PEM_read_RSA_PUBKEY(file, NULL, NULL, NULL)) == NULL) {
        ERR_print_errors_fp(stdout);
        goto End;
    }

    length = strlen(data);

    encrypted_data = (char *)malloc(256);
    if (!encrypted_data) {
        perror("malloc() error");
        goto End;
    }

    memset(encrypted_data, 0, 256);

    // 3. encrypt
    // RSA/None/PKCS1Padding
    int retCount = RSA_public_encrypt(length, (unsigned char *)data, (unsigned char *)encrypted_data, rsa_key, RSA_PKCS1_PADDING);
    printf("encrypt retCount is:%d \n", retCount);
    if (retCount < 0) {
        perror("RSA_public_encrypt()");
        goto End;
    }

End:
    if (rsa_key) {
        RSA_free(rsa_key);
    }
    if (file) {
        fclose(file);
    }
    encrypt.len = retCount;
    encrypt.data = encrypted_data;
    return encrypt;
}

//解密
char *rsa_decrypt(char *str, char *path_key) {
    char *p_de = NULL;
    RSA *p_rsa = NULL;
    FILE *file = NULL;
    int rsa_len = 0;

    // 1.打开秘钥文件
    file = fopen(path_key, "rb");
    if (!file) {
        perror("fopen() error 22222222222");
        goto End;
    }

    // 2.从私钥中获取 解密的秘钥
    if ((p_rsa = PEM_read_RSAPrivateKey(file, NULL, NULL, NULL)) == NULL) {
        ERR_print_errors_fp(stdout);
        goto End;
    }

    // 3.获取秘钥的长度，
    rsa_len = RSA_size(p_rsa);

    // 4.为加密后的内容 申请空间（根据秘钥的长度+1）
    p_de = (char *)malloc(rsa_len + 1);
    if (!p_de) {
        perror("malloc() error ");
        goto End;
    }
    memset(p_de, 0, rsa_len + 1);

    // 5.对内容进行加密
    if (RSA_private_decrypt(rsa_len, (unsigned char *)str, (unsigned char *)p_de, p_rsa, RSA_PKCS1_PADDING) < 0) {
        perror("RSA_public_encrypt() error ");
        goto End;
    }

End:
    // 6.释放秘钥空间， 关闭文件
    if (p_rsa) RSA_free(p_rsa);
    if (file) fclose(file);

    return p_de;
}

/************************************************************************
** 本例采用：
** 3des-ecb加密方式；
** 24位密钥，不足24位的右补0x00；
** 加密内容8位补齐，补齐方式为：少1位补一个0x01,少2位补两个0x02,...
** 本身已8位对齐的，后面补八个0x08。
************************************************************************/
Encrypt desEncrypt(char *desPassword, char *data, int length) {
    int docontinue = 1;
    int data_len;
    int data_rest;
    unsigned char ch;
    unsigned char *src = NULL;        /* 补齐后的明文 */
    unsigned char *dst = NULL;        /* 解密后的明文 */
    unsigned char *outEncrypt = NULL; /* 解密后的明文 */
    int len;
    unsigned char tmp[8];
    unsigned char in[8];
    unsigned char out[8];
    // char *k = "01234567899876543210"; /* 原始密钥 */
    int key_len;
#define LEN_OF_KEY 24
    unsigned char key[LEN_OF_KEY]; /* 补齐后的密钥 */
    unsigned char block_key[9];
    DES_key_schedule ks, ks2, ks3;
    /* 构造补齐后的密钥 */
    key_len = strlen(desPassword);
    memcpy(key, desPassword, key_len);
    memset(key + key_len, 0x00, LEN_OF_KEY - key_len);
    /* 分析补齐明文所需空间及补齐填充数据 */
    data_len = length;
    data_rest = data_len % 8;
    len = data_len + (8 - data_rest);
    ch = 8 - data_rest;
    src = (unsigned char *)malloc(len);
    dst = (unsigned char *)malloc(len);
    outEncrypt = (unsigned char *)malloc(len);
    if (NULL == src || NULL == dst) {
        docontinue = 0;
    }
    if (docontinue) {
        int count;
        int i;
        /* 构造补齐后的加密内容 */
        memset(src, 0, len);
        memcpy(src, data, data_len);
        memset(src + data_len, ch, 8 - data_rest);
        /* 密钥置换 */
        memset(block_key, 0, sizeof(block_key));
        memcpy(block_key, key + 0, 8);
        DES_set_key_unchecked((const_DES_cblock *)block_key, &ks);
        memcpy(block_key, key + 8, 8);
        DES_set_key_unchecked((const_DES_cblock *)block_key, &ks2);
        memcpy(block_key, key + 16, 8);
        DES_set_key_unchecked((const_DES_cblock *)block_key, &ks3);
        // printf("before encrypt:\n");
        // for (i = 0; i < len; i++) {
        //     printf("0x%.2X ", *(src + i));
        // }
        // printf("\n");
        /* 循环加密/解密，每8字节一次 */
        count = len / 8;
        for (i = 0; i < count; i++) {
            memset(tmp, 0, 8);
            memset(in, 0, 8);
            memset(out, 0, 8);
            memcpy(tmp, src + 8 * i, 8);
            /* 加密 */
            DES_ecb3_encrypt((const_DES_cblock *)tmp, (DES_cblock *)in, &ks, &ks2, &ks3, DES_ENCRYPT);
            /* 将加密的内容拷贝到加密后的内存 */
            memcpy(outEncrypt + 8 * i, in, 8);
            // /* 解密 */
            // DES_ecb3_encrypt((const_DES_cblock *)in, (DES_cblock *)out, &ks, &ks2, &ks3, DES_DECRYPT);
            // /* 将解密的内容拷贝到解密后的明文 */
            // memcpy(dst + 8 * i, out, 8);
        }
        // printf("after encrypt:\n");
        // for (i = 0; i < len; i++) {
        //     printf("%.2X ", *(outEncrypt + i));
        // }
        // printf("\n");

        // printf("after decrypt :\n");
        // for (i = 0; i < len; i++) {
        //     printf("0x%.2X ", *(dst + i));
        // }
        // printf("\n");
    }
    if (NULL != src) {
        free(src);
        src = NULL;
    }
    if (NULL != dst) {
        free(dst);
        dst = NULL;
    }
    // if (NULL != dst) {
    //     free(dst);
    //     dst = NULL;
    // }
    Encrypt encrypt;
    encrypt.len = len;
    encrypt.data = outEncrypt;
    return encrypt;
}
/************************************************************************
** 本例采用：
** 3des-ecb加密方式；
** 24位密钥，不足24位的右补0x00；
** 加密内容8位补齐，补齐方式为：少1位补一个0x01,少2位补两个0x02,...
** 本身已8位对齐的，后面补八个0x08。
************************************************************************/
Encrypt desDecrypt(char *desPassword, char *data, int length) {
    int docontinue = 1;
    unsigned char *src = NULL; /* 补齐后的明文 */
    unsigned char *dst = NULL; /* 解密后的明文 */
    int len;
    unsigned char tmp[8];
    unsigned char in[8];
    unsigned char out[8];
    int key_len;
#define LEN_OF_KEY 24
    unsigned char key[LEN_OF_KEY]; /* 补齐后的密钥 */
    unsigned char block_key[9];
    DES_key_schedule ks, ks2, ks3;
    /* 构造补齐后的密钥 */
    key_len = strlen(desPassword);
    memcpy(key, desPassword, key_len);
    memset(key + key_len, 0x00, LEN_OF_KEY - key_len);
    /* 分析补齐明文所需空间及补齐填充数据 */
    len = length;
    src = (unsigned char *)malloc(len);
    dst = (unsigned char *)malloc(len);
    memcpy(src, data, len);
    if (NULL == src || NULL == dst) {
        docontinue = 0;
    }
    if (docontinue) {
        int count;
        int i;
        /* 密钥置换 */
        memset(block_key, 0, sizeof(block_key));
        memcpy(block_key, key + 0, 8);
        DES_set_key_unchecked((const_DES_cblock *)block_key, &ks);
        memcpy(block_key, key + 8, 8);
        DES_set_key_unchecked((const_DES_cblock *)block_key, &ks2);
        memcpy(block_key, key + 16, 8);
        DES_set_key_unchecked((const_DES_cblock *)block_key, &ks3);

        /* 循环加密/解密，每8字节一次 */
        count = len / 8;
        for (i = 0; i < count; i++) {
            memset(tmp, 0, 8);
            memset(out, 0, 8);
            memcpy(tmp, src + 8 * i, 8);
            /* 解密 */
            DES_ecb3_encrypt((const_DES_cblock *)tmp, (DES_cblock *)out, &ks, &ks2, &ks3, DES_DECRYPT);
            /* 将解密的内容拷贝到解密后的明文 */
            memcpy(dst + 8 * i, out, 8);
        }
    }
    if (NULL != src) {
        free(src);
        src = NULL;
    }
    // if (NULL != dst) {
    //     free(dst);
    //     dst = NULL;
    // }
    //去除填充的字节
    unsigned char x1 = dst[len - 1];
    unsigned char x2 = dst[len - 2];
    if (x1 == 1 || x1 == x2) {
        len -= x1;
    }
    for (int i = 0; i < x1; i++) {
        dst[len + i] = 0;
    }
    Encrypt encrypt;
    encrypt.len = len;
    encrypt.data = dst;
    return encrypt;
}
